use std::borrow::Cow;

use crate::{
    globals::POOL_RUNTIME,
    pools::{HasPoolHandle, PoolEntry},
    values::ArrayDType,
};

/** StringStore 是字符串数据的存储格子
 *
 * 对 Python 来说，字符串的 FFI 总是拷贝，因为不是 UTF-8 的，此时 drop_callback/object_handle 是空的。
 * 对于 Julia 来说可以避免拷贝，此时需要提供 drop_callback/object_handle 来释放内存。
 *
 */
pub struct StringBox {
    pub handle: usize,
    pub data: Cow<'static, str>,
    pub length: u64, // 字符串长度，单位是字节
    pub foreign_ob: *mut std::ffi::c_void,
    pub runtime_id: u32,
}

impl HasPoolHandle for StringBox {
    fn fill_handle(&mut self, handle: usize) {
        self.handle = handle;
    }
}

impl Drop for StringBox {
    fn drop(&mut self) {
        if self.foreign_ob.is_null() {
            return;
        }
        let rt_lock = POOL_RUNTIME.read();
        let rt = rt_lock.get(PoolEntry::new(self.runtime_id as usize));
        if let Some(rt) = rt {
            if rt.thread_safe {
                if let Some(drop_callback) = rt.string_drop_callback {
                    drop_callback(self.foreign_ob);
                }
            } else {
                rt.thread_defer_dealloc_strings.push(self.foreign_ob);
            }
        } else {
            println!("String memory leak: runtime not found");
        }
    }
}

unsafe impl Send for StringBox {}
unsafe impl Sync for StringBox {}

pub struct ArrayViewBox {
    pub handle: usize,
    pub data: *const std::ffi::c_void,
    pub ndims: u64,
    pub dimensions: Vec<usize>,
    pub strides: Vec<isize>,
    pub dtype: ArrayDType,
    pub foreign_ob: *mut std::ffi::c_void,
    pub runtime_id: u32,
    pub readonly: bool,
    pub fortran_order: bool,
}

impl HasPoolHandle for ArrayViewBox {
    fn fill_handle(&mut self, handle: usize) {
        self.handle = handle;
    }
}

impl Drop for ArrayViewBox {
    fn drop(&mut self) {
        if self.foreign_ob.is_null() {
            return;
        }
        let rt_lock = POOL_RUNTIME.read();
        let rt: Option<&crate::runtime::Runtime> =
            rt_lock.get(PoolEntry::new(self.runtime_id as usize));

        if let Some(rt) = rt {
            let Some(drop_callback) = rt.array_drop_callback else {
                return;
            };

            if rt.thread_safe {
                drop(rt_lock);
                drop_callback(self.foreign_ob);
            } else {
                let try_lock = rt.lock.clone();
                let try_lock = try_lock.try_lock();
                if try_lock.is_some() {
                    drop(rt_lock);
                    drop_callback(self.foreign_ob);
                } else {
                    rt.thread_defer_dealloc_arrays.push(self.foreign_ob);
                    drop(try_lock);
                }
            }
        } else {
            println!("Array memory leak: runtime not found");
        }
    }
}

unsafe impl Send for ArrayViewBox {}
unsafe impl Sync for ArrayViewBox {}

#[derive(Clone)]
pub struct Complex64Box {
    pub real: f64,
    pub imag: f64,
}

pub struct FunctionBox {
    pub runtime_id: usize,
    pub foreign_ob: *mut std::ffi::c_void,
}
